
`include "common_header.verilog"

//  *************************************************************************
//  File : stats_vectoaddrs
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized Reproduction or Use is Expressly Prohibited.
//  Copyright (c) 2009 MoreThanIP.com, Germany
//  Designed by : Daniel Koehler
//  support@morethanip.com
//  *************************************************************************
//  Description: Implement read/modify/write counters. Creates the addresses
//               for each counter found until all counters in the vector are
//               served.
//  Version    : $Id: stats_vectoaddrs.v,v 1.2 2013/09/09 09:50:48 dk Exp $
//  *************************************************************************

module stats_vectoaddrs (

   reset,
   clk,
   stat_val,
   stat,
   stat_wait,
   caddr,
   caddr_val,
   cbusy,
   vec_busy_int);
   
parameter CNTNUM        = 32;   // width of vector
parameter CNTADDR       = 5;    // address bits   

input   reset;                  //  Active High reset
input   clk;                    //  Active High reset for xgmii_txclk domain
input   stat_val;               //  Statistic Vector Valid. 
input   [CNTNUM - 1:0] stat;    //  Statistic Vector. Must stay valid until all processing done.
input   stat_wait;              // can be used to delay the start of the burst
output  [CNTADDR - 1:0] caddr;  //  counter address
output  caddr_val;              //  counter address valid, should increment
output  cbusy;                  //  processing is ongoing, asserts following stat_val
output  vec_busy_int;           //  processing is ongoing, asserts following stat_val, deasserts delayed but is registered

reg     [CNTADDR - 1:0] caddr; 
reg     caddr_val; 
wire    cbusy; 
wire    vec_busy_int;

reg     busy_int; 
reg     busy_int_d; 
wire    [CNTNUM - 1:0] mask_next; //  AND mask for next step
reg     [CNTNUM - 1:0] stat_masked; //  served bits removed
wire    [CNTNUM - 1:0] stat_masked_next; //  served bits removed
wire    stat_masked_any; //  at least one bit is set (OR_REDUCE)
reg     [CNTNUM - 1:0] stat_masked_r; //  registered (timing improvement)

// select the next bit. Scan the current vector until the first nonzero bit and returns a vector with just this bit being set.
// --------------------------------------------------------------------------
function [CNTNUM - 1:0] SEL_BIT(input [CNTNUM - 1:0] reqmask); 
reg     [CNTNUM - 1:0]  sel_mask; 
integer sbi;
begin

        sel_mask = {CNTNUM{1'b 0}};

        for(sbi=0; sbi < CNTNUM; sbi=sbi+1)
        begin
        
                if( (|(sel_mask))==1'b 0 & reqmask[sbi]==1'b 1 )
                begin                        
                        sel_mask[sbi] = 1'b 1;
                end   
                else
                begin
                        sel_mask[sbi] = 1'b 0;
                end
        end
        
        SEL_BIT = sel_mask;

end
endfunction

// encoder. Scans the vector to find the lowest bit set and returns its number.
// --------------------------------------------------------------------
function [CNTADDR - 1:0] SEL_NUM(input [CNTNUM - 1:0] reqmask); 
integer sni;
begin

        SEL_NUM = {CNTADDR{1'b 0}};
        
        for( sni=0; sni < CNTNUM; sni=sni+1 )
        begin
                // scan from top to bottom, last bit wins then
                if( reqmask[CNTNUM-1-sni]==1'b 1 )
                begin
                        SEL_NUM = (CNTNUM-1-sni);
                end
        end

end
endfunction



//  encoder. Scans the vector to find the bit set and returns its number.
//  --------------------------------------------------------------------

assign mask_next        = SEL_BIT(stat_masked); 
assign stat_masked_next = stat_masked & ~mask_next; //  remove the served bit
assign stat_masked_any  = |(stat_masked); //  control

//  -------        
always @(posedge reset or posedge clk)
   begin : process_1
   if (reset == 1'b 1)
      begin
      busy_int <= 1'b 0;	
      busy_int_d <= 1'b 0;	
      stat_masked <= {(CNTNUM){1'b 0}};	
      end
   else
      begin
        //  indicate as long as there are pending bits, but guarantee to deassert always after processing was done 
      if (stat_val == 1'b 1 & busy_int == 1'b 0)
         begin
         busy_int <= 1'b 1;	
         end
      else if (stat_masked_any == 1'b 0 )
         begin
                //  if no more counter is selected we are done
         busy_int <= 1'b 0;	
         end

      if (stat_masked_any == 1'b 0)
         begin
                //  if no more counter is selected we are done
         busy_int_d <= 1'b 0;	
         end
      else if(stat_wait == 1'b 0)
         begin
         busy_int_d <= busy_int;	
         end


        //  load and modify the stats as we process them
      if (busy_int == 1'b 0)
         begin
         stat_masked <= stat;	
         end
      else if( stat_wait==1'b 0 | busy_int_d==1'b 1 )
         begin
         stat_masked <= stat_masked_next;	//  remove the served bit
         end

      end
   end

assign vec_busy_int = busy_int;
assign cbusy = stat_masked_any; //  '0' when stat_masked=stat_zero else '1';

//  create outputs
//  --------------        
always @(posedge reset or posedge clk)
   begin : process_2
   if (reset == 1'b 1)
      begin
      stat_masked_r <= {(CNTNUM){1'b 0}};	
      caddr <= {(CNTADDR){1'b 0}};	
      caddr_val <= 1'b 0;	
      end
   else
      begin

      stat_masked_r <= stat_masked;	        //  timing optimization
      caddr         <= SEL_NUM(stat_masked_r);	//  encode the set bit

        //  indicate if the address is valid and the counter should increment

      if (busy_int_d == 1'b 1)
         begin
         caddr_val <= 1'b 1;
         end
      else
         begin
         caddr_val <= 1'b 0;	
         end

      end
   end

// ----------
// ASSERTIONS
// ----------
`ifdef MTIPASSERTIONS
// synopsys translate_off
// synthesis translate_off

//always @(posedge reset or negedge clk)
//begin : assertions
//        if (reset == 1'b 1)
//        begin
//        end
//        else
//        begin
//                // ensure no new data while increment is running
//                if( busy_int==1 )
//                begin
//                        if(! (stat_val==0) )
//                        begin
//                        $display("ASSERT ***[stats_vectoaddrs]: ERROR: New statistics vector arrived while current is in progress. This vector will be lost.");
//                        $stop(1);
//                        end
//                end
//
//        end
//end

always @(negedge stat_val)
begin:assertions
        if( !reset )
        begin
                if( busy_int_d )     // if a pulse was ignored by us here
                begin
                        $display("ASSERT ***[stats_vectoaddrs]: ERROR: New statistics vector arrived while current is in progress. This vector will be lost.");
                        `ifdef MTIPASSERTIONS_STOP_ON_ERRORS
                        $stop(1);
                        `endif
                end
        end
end
        
// synthesis translate_on
// synopsys translate_on
`endif


endmodule // module stats_vectoaddrs

